package com.dannypatterson.airinstaller {
	
	[Event(name="init", type="flash.events.Event")]
	
	import com.dannypatterson.airinstaller.events.BrowserInstallerEvent;
	
	import flash.display.Loader;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.LocalConnection;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	
	/**
	 * This class is used to wrap the functionality of the AIR Browser API provided by Adobe.
	 * It also adds notification events for when the applications loads after launch
	 * or install/update.
	 * 
	 * @author Danny Patterson
	 */
	public class BrowserInstaller extends EventDispatcher {
		
		static public const INSTALLED:String = "installed";
		static public const AVAILABLE:String = "available";
		static public const UNAVAILABLE:String = "unavailable";
		
		static private const BROWSER_API_SWF_URL:String = "http://airdownload.adobe.com/air/browserapi/air.swf"; 
		static private var airSWF:Object;
		static private var airSWFLoader:Loader;
		
		private var applicationURL:String;
		private var applicationId:String;
		private var publisherId:String;
		private var airVersion:String;
		private var installedApplicationVersion:String;
		private var notifyConnection:LocalConnection;
		private var _isInited:Boolean;
		
		/**
		 * @param applicationURL (String) The URL of the air file you want to install/update.
		 * @param applicationId (String) The ID of the AIR application you want to install/update/launch.
		 * @param publisherId (String) The publisher ID of the AIR application you want to install/update/launch.
		 * @param airVersion (String) [1.0] The minimum AIR runtime version used in the AIR application you want to install/update/launch. This parameter defaults to 1.0.
		 */
		public function BrowserInstaller(applicationURL:String, applicationId:String, publisherId:String, airVersion:String = "1.0") {
			this.applicationURL = applicationURL;
			this.applicationId = applicationId;
			this.publisherId = publisherId;
			this.airVersion = airVersion;
			if(BrowserInstaller.airSWFLoader == null) {
				BrowserInstaller.airSWFLoader = new Loader();
				BrowserInstaller.airSWFLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onAirSWFLoadComplete, false, 0, true);
				// TODO: Listen for an error while loading the SWF
				BrowserInstaller.airSWFLoader.load(new URLRequest(BrowserInstaller.BROWSER_API_SWF_URL), new LoaderContext(false, ApplicationDomain.currentDomain));
			}else if(BrowserInstaller.airSWF == null) {
				BrowserInstaller.airSWFLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onAirSWFLoadComplete, false, 0, true);
			}else {
				onAirSWFLoadComplete();
			}
			notifyConnection = new LocalConnection();
			notifyConnection.allowDomain("*");
			notifyConnection.client = this;
			notifyConnection.connect("_launchNotify");
		}
		
		private function onAirSWFLoadComplete(event:Event = null):void {
			if(BrowserInstaller.airSWF == null) {
				BrowserInstaller.airSWF = (BrowserInstaller.airSWFLoader.content);
			}
			if(BrowserInstaller.airSWF.getStatus() == BrowserInstaller.INSTALLED) {
				BrowserInstaller.airSWF.getApplicationVersion(applicationId, publisherId, onAppVersionComplete);
			}else {
				onAppVersionComplete();
			}
		}
		
		private function onAppVersionComplete(version:String = null):void {
			installedApplicationVersion = version;
			_isInited = true;
			dispatchEvent(new BrowserInstallerEvent(BrowserInstallerEvent.INIT));
		}
		
		private function onInstalledVersionCheckComplete(version:String):void {
			installedApplicationVersion = version;
			dispatchEvent(new BrowserInstallerEvent(BrowserInstallerEvent.INSTALL_COMPLETE));
		}
		
		public function onLaunchNotify(applicationId:String):void {
			// NOTE: This method is public because its exposed via LocalConnection
			if(applicationId == this.applicationId) {
				dispatchEvent(new BrowserInstallerEvent(BrowserInstallerEvent.LAUNCH_COMPLETE));
			}
		}
		
		public function onInstallNotify(applicationId:String):void {
			// NOTE: This method is public because its exposed via LocalConnection
			if(applicationId == this.applicationId) {
				BrowserInstaller.airSWF.getApplicationVersion(applicationId, publisherId, onInstalledVersionCheckComplete);
			}
		}
		
		/**
		 * Returns the version of the AIR application if it is installed.  If the value is null,
		 * the application is not installed.
		 */
		public function getApplicationVersion():String {
			if(!isInited()) throw new IllegalOperationError("Installer has not yet initialized.");
			return installedApplicationVersion;
		}
		
		/**
		 * Returns the installed status of the AIR runtime.  The following are the constants that
		 * match the returned value and what each mean:
		 * 	 AIRInstaller.INSTALLED - The AIR Runtime is installed.
		 * 	 AIRInstaller.AVAILABLE - The AIR Runtime is not installed but is available for this operating system.
		 * 	 AIRInstaller.UNAVAILABLE - The AIR Runtime is not available for this operating system.
		 * 
		 * @return (String)
		 */
		public function getRuntimeStatus():String {
			if(!isInited()) throw new IllegalOperationError("Installer has not yet initialized.");
			return BrowserInstaller.airSWF.getStatus();
		}
		
		/**
		 * Launches the install process.  If the runtime is not installed, it will first install
		 * the runtime and then the application otherwise it will just install the application.
		 * 
		 * @param arguments (Array) Optional arguments to pass to the application if it is launched upon installation.
		 */
		public function install(arguments:Array = null):void {
			if(!isInited()) throw new IllegalOperationError("Installer has not yet initialized.");
			if(arguments == null) arguments = new Array();
			arguments.push("installNotify");
			BrowserInstaller.airSWF.installApplication(applicationURL, airVersion, arguments);
		}
		
		/**
		 * Method exposes if the Installer is initialized.  In order to initialize both the AIR
		 * Browser API SWF and the application version checks must be complete.
		 */
		public function isInited():Boolean {
			return _isInited;
		}
		
		/**
		 * Launches the application if its installed and the allowBrowserInvocation is enabled.
		 * 
		 * @param arguments (Array) Optional arguments to pass to the application when its is launched.
		 */
		public function launch(arguments:Array = null):void {
			if(!isInited()) throw new IllegalOperationError("Installer has not yet initialized.");
			if(arguments == null) arguments = new Array();
			arguments.push("launchNotify");
			BrowserInstaller.airSWF.launchApplication(applicationId, publisherId, arguments);
		}

	}
	
}